





/*******************************************************************************
 *                                    ZLG
 *                         ----------------------------
 *                         innovating embedded platform
 *
 * Copyright (c) 2001-present Guangzhou ZHIYUAN Electronics Co., Ltd.
 * All rights reserved.
 *
 * Contact information:
 * web site:    https://www.zlg.cn
 *******************************************************************************/
/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "core/include/hpm6e00_clk.h"

/*******************************************************************************
 * Marco operate
 ******************************************************************************/
#define FREQ_1MHz              (1000000UL)
#define FREQ_PRESET1_OSC0_CLK0 (24000000UL)

#define PLLCTLV2_PLL_XTAL_FREQ (24000000UL)

#define SYSCTL_RESOURCE_GROUP0  0
#define SYSCTL_RESOURCE_GROUP1  1

/*******************************************************************************
 * Global
 ******************************************************************************/
uint32_t __g_hpm_core_clk;

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief 核心时钟更新
 */
void clk_core_clk_update(void){
    uint32_t     hart_id      = read_csr(CSR_MHARTID);
    clock_node_t cpu_clk_node = (hart_id == 1U) ? clock_node_cpu1 : clock_node_cpu0;

    __g_hpm_core_clk = clk_freq_get(CLK_SRC_GROUP_COMMON, cpu_clk_node);
}

/**
 * \brief PLL 频率获取
 */
static uint32_t __pllctlv2_pll_freq_get(pllctlv2_pll_t pll){
    uint32_t freq = 0;

    if (pll < PLLCTL_SOC_PLL_MAX_COUNT) {
        uint32_t mfi = PLLCTLV2_PLL_MFI_MFI_GET(HPM_PLL_CTLV2->PLL[pll].MFI);
        uint32_t mfn = PLLCTLV2_PLL_MFN_MFN_GET(HPM_PLL_CTLV2->PLL[pll].MFN);
        uint32_t mfd = PLLCTLV2_PLL_MFD_MFD_GET(HPM_PLL_CTLV2->PLL[pll].MFD);
        freq = (uint32_t) (PLLCTLV2_PLL_XTAL_FREQ * (mfi + 1.0 * mfn / mfd));
    }
    return freq;
}

/**
 * \brief PLL 频率获取
 */
static uint32_t __pll_postdiv_freq_get(pllctlv2_pll_t pll, pllctlv2_clk_t clk){
    uint32_t postdiv_freq = 0;

    if (pll < PLLCTL_SOC_PLL_MAX_COUNT) {
        uint32_t postdiv = PLLCTLV2_PLL_DIV_DIV_GET(HPM_PLL_CTLV2->PLL[pll].DIV[clk]);
        uint32_t pll_freq = __pllctlv2_pll_freq_get(pll);
        postdiv_freq = (uint32_t) (pll_freq / (1U + postdiv * 1.0 / 5U));
    }

    return postdiv_freq;
}

/**
 * \brief 获取时钟源时钟频率
 *
 * \param[in] clk_src 时钟源
 *
 * \retval 成功返回时钟频率
 */
uint32_t clk_source_freq_get(clock_source_t clk_src){
    uint32_t clk_freq = 0UL;

    switch (clk_src) {
    case clock_source_osc0_clk0:
        clk_freq = FREQ_PRESET1_OSC0_CLK0;
        break;
    case clock_source_pll0_clk0:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll0, pllctlv2_clk0);
        break;
    case clock_source_pll0_clk1:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll0, pllctlv2_clk1);
        break;
    case clock_source_pll1_clk0:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll1, pllctlv2_clk0);
        break;
    case clock_source_pll1_clk1:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll1, pllctlv2_clk1);
        break;
    case clock_source_pll1_clk2:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll1, pllctlv2_clk2);
        break;
    case clock_source_pll2_clk0:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll2, pllctlv2_clk0);
        break;
    case clock_source_pll2_clk1:
        clk_freq = __pll_postdiv_freq_get(pllctlv2_pll2, pllctlv2_clk1);
        break;
    default:
        clk_freq = 0UL;
        break;
    }

    return clk_freq;
}

/**
 * \brief 通用组 IP 时钟频率获取
 */
static uint32_t __common_group_ip_freq_get(clock_node_t clk_node){
    uint32_t clk_freq = 0;

    if (clk_node < clock_node_end) {
        /* 获取时钟分频*/
        uint32_t clk_div = 1UL + SYSCTL_CLOCK_DIV_GET(HPM_SYS_CTL->CLOCK[clk_node]);
        /* 获取时钟源*/
        clock_source_t clk_mux = (clock_source_t)SYSCTL_CLOCK_MUX_GET(HPM_SYS_CTL->CLOCK[clk_node]);
        /* 获取时钟源频率*/
        clk_freq = clk_source_freq_get(clk_mux) / clk_div;
    }
    return clk_freq;
}

/**
 * \brief 获取时钟频率
 *
 * \param[in] clk_src_type 时钟源类型
 * \param[in] clk_node     时钟节点
 *
 * \retval 成功返回时钟频率
 */
uint32_t clk_freq_get(uint8_t clk_src_type, clock_node_t clk_node){
    uint32_t clk_freq = 0;

    switch (clk_src_type) {
        case CLK_SRC_GROUP_COMMON:
            clk_freq = __common_group_ip_freq_get(clk_node);
            break;
    case CLK_SRC_GROUP_ADC:
//        clk_freq = get_frequency_for_adc(node_or_instance);
        break;
    case CLK_SRC_GROUP_I2S:
//        clk_freq = get_frequency_for_i2s(node_or_instance);
        break;
    case CLK_SRC_GROUP_EWDG:
//        clk_freq = get_frequency_for_ewdg(node_or_instance);
        break;
    case CLK_SRC_GROUP_PEWDG:
//        clk_freq = get_frequency_for_pewdg();
        break;
    case CLK_SRC_GROUP_PMIC:
        clk_freq = FREQ_PRESET1_OSC0_CLK0;
        break;
    case CLK_SRC_GROUP_AHB:
        clk_freq = __common_group_ip_freq_get(clock_node_ahb0);
        break;
    case CLK_SRC_GROUP_AXIF:
        clk_freq = __common_group_ip_freq_get(clock_node_axif);
        break;
    case CLK_SRC_GROUP_AXIS:
        clk_freq = __common_group_ip_freq_get(clock_node_axis);
        break;
    case CLK_SRC_GROUP_AXIC:
        clk_freq = __common_group_ip_freq_get(clock_node_axic);
        break;
    case CLK_SRC_GROUP_AXIN:
        clk_freq = __common_group_ip_freq_get(clock_node_axin);
        break;
    case CLK_SRC_GROUP_CPU0:
        clk_freq = __common_group_ip_freq_get(clock_node_cpu0);
        break;
    case CLK_SRC_GROUP_CPU1:
        clk_freq = __common_group_ip_freq_get(clock_node_cpu1);
        break;
    case CLK_SRC_GROUP_SRC:
        clk_freq = clk_source_freq_get((clock_source_t)clk_node);
        break;
    default:
        clk_freq = 0UL;
        break;
    }
    return clk_freq;
}

/**
 * \brief 组时钟源使能
 */
static int __sysctl_group_resource_enable(uint8_t           group,
                                          sysctl_resource_t linkable_resource,
                                          bool_t            is_enable){
    uint32_t idx, offset;

    if (linkable_resource < sysctl_resource_linkable_start) {
        return -EINVAL;
    }

    idx    = (linkable_resource - sysctl_resource_linkable_start) / 32;
    offset = (linkable_resource - sysctl_resource_linkable_start) % 32;
    switch (group) {
        case SYSCTL_RESOURCE_GROUP0:
            WRITE_REG32(HPM_SYS_CTL->GROUP0[idx].VALUE, (HPM_SYS_CTL->GROUP0[idx].VALUE & ~(1UL << offset))
                    | (is_enable ? (1UL << offset) : 0));
            if (is_enable) {
                while (sysctl_resource_target_is_busy(linkable_resource)) {
                    ;
                }
            }
            break;
        case SYSCTL_RESOURCE_GROUP1:
            WRITE_REG32(HPM_SYS_CTL->GROUP1[idx].VALUE, (HPM_SYS_CTL->GROUP1[idx].VALUE & ~(1UL << offset))
                    | (is_enable ? (1UL << offset) : 0));
            if (is_enable) {
                while (sysctl_resource_target_is_busy(linkable_resource)) {
                    ;
                }
            }
            break;
        default:
            return -EINVAL;;
    }

    return 0;
}

/**
 * \brief 逻辑组添加时钟源
 *
 * \param[in] clk_resource 时钟源
 * \param[in] group        要添加的组
 */
void clk_group_add(sysctl_resource_t clk_resource, uint32_t group){
    if (clk_resource < sysctl_resource_end) {
        __sysctl_group_resource_enable(group, clk_resource, TRUE);
    }
}

/**
 * \brief 连接逻辑组到 CPU
 *
 * \param[in] group   连接组
 * \param[in] cpu_idx CPU 索引
 */
void clk_group_cpu_connect(uint32_t group, uint32_t cpu_idx){
    if (cpu_idx < 2U) {
        WRITE_REG32(HPM_SYS_CTL->AFFILIATE[cpu_idx].SET, (1UL << group));
    }
}

/**
 * \brief 时钟配置
 */
static int __sysctl_clk_cfg(clock_node_t   clk_node,
                            clock_source_t clk_src,
                            uint32_t       div){
    if (clk_node >= clock_node_adc_start) {
        return -EINVAL;
    }

    if (clk_src >= clock_source_general_source_end) {
        return -EINVAL;
    }

    CLEAR_REG32_BIT(HPM_SYS_CTL->CLOCK[clk_node], (SYSCTL_CLOCK_MUX_MASK | SYSCTL_CLOCK_DIV_MASK));
    SET_REG32_BIT(HPM_SYS_CTL->CLOCK[clk_node], (SYSCTL_CLOCK_MUX_SET(clk_src) | SYSCTL_CLOCK_DIV_SET(div - 1)));

    while(sysctl_clock_target_is_busy(clk_node));

    if ((clk_node == clock_node_cpu0) || (clk_node == clock_node_cpu1)) {
        clk_core_clk_update();
    }

    return 0;
}

/**
 * \brief 设置时钟源分频
 *
 * \param[in] clk_node     时钟节点
 * \param[in] clk_src_type 时钟源类型
 * \param[in] clk_src      时钟源
 * \param[in] div          时钟分频
 *
 * \retval 成功返回 0
 */
int clk_source_divider_set(clock_node_t   clk_node,
                           uint8_t        clk_src_type,
                           clock_source_t clk_src,
                           uint32_t       div){
    int ret = 0;

    switch (clk_src_type) {
        case CLK_SRC_GROUP_COMMON:
            if ((div < 1U) || (div > 256U)) {
                ret = -EINVAL;
            } else {
                __sysctl_clk_cfg(clk_node, clk_src, div);
            }
            break;
        case CLK_SRC_GROUP_ADC:
        case CLK_SRC_GROUP_I2S:
        case CLK_SRC_GROUP_EWDG:
        case CLK_SRC_GROUP_PEWDG:
        case CLK_SRC_GROUP_SRC:
        case CLK_SRC_GROUP_PMIC:
        case CLK_SRC_GROUP_AHB:
        case CLK_SRC_GROUP_AXIF:
        case CLK_SRC_GROUP_AXIS:
        case CLK_SRC_GROUP_AXIC:
        case CLK_SRC_GROUP_AXIN:
        case CLK_SRC_GROUP_CPU0:
        case CLK_SRC_GROUP_CPU1:
            ret = -ENOTSUP;
            break;
        default:
            ret = -EINVAL;;
            break;
    }

    return ret;

}

/**
 * \brief 获取每微秒有多少个时钟 tick
 */
static uint32_t __core_clk_ticks_per_us_get(void){
    if (__g_hpm_core_clk == 0U) {
        clk_core_clk_update();
    }
    return (__g_hpm_core_clk + FREQ_1MHz - 1U) / FREQ_1MHz;
}

/**
 * \brief 获取每毫秒有多少个时钟 tick
 */
static uint32_t __core_clk_ticks_per_ms_get(void){
    if (__g_hpm_core_clk == 0U) {
        clk_core_clk_update();
    }
    return (__g_hpm_core_clk + FREQ_1MHz - 1U) / 1000;
}

static uint64_t __csr_get_core_cycle(void){
    uint64_t result;
    uint32_t resultl_first  = read_csr(CSR_CYCLE);
    uint32_t resulth        = read_csr(CSR_CYCLEH);
    uint32_t resultl_second = read_csr(CSR_CYCLE);

    if (resultl_first < resultl_second) {
        result = ((uint64_t)resulth << 32) | resultl_first; /* if CYCLE didn't roll over, return the value directly */
    } else {
        resulth = read_csr(CSR_CYCLEH);
        result = ((uint64_t)resulth << 32) | resultl_second; /* if CYCLE rolled over, need to get the CYCLEH again */
    }
    return result;
}

/**
 * \brief 延时微秒函数
 *
 * \param[in] us 要延时的微秒
 */
void clk_cpu_delay_us(uint32_t us){
    uint64_t expected_ticks = __csr_get_core_cycle() + (uint64_t)__core_clk_ticks_per_us_get() * (uint64_t)us;
    while (__csr_get_core_cycle() < expected_ticks) {
    }
}

/**
 * \brief 延时毫秒函数
 *
 * \param[in] us 要延时的毫秒
 */
void clk_cpu_delay_ms(uint32_t ms){
    uint64_t expected_ticks = __csr_get_core_cycle() + (uint64_t)__core_clk_ticks_per_ms_get() * (uint64_t)ms;
    while (__csr_get_core_cycle() < expected_ticks) {
    }
}

